/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

import { customElement, property, observe } from '@polymer/decorators';
import { html, PolymerElement } from '@polymer/polymer';
import * as _ from 'lodash';
import { DarkModeMixin } from '../polymer/dark_mode_mixin';
import '../polymer/irons_and_papers';
import { LegacyElementMixin } from '../polymer/legacy_element_mixin';
import * as tb_debug from '../tb_debug';
import '../tf_dashboard_common/tensorboard-color';
import * as tf_graph_common from '../tf_graph_common/common';
import * as tf_graph_proto from '../tf_graph_common/proto';
import * as tf_graph_render from '../tf_graph_common/render';
import '../tf_graph_common/tf-graph-icon';
import * as tf_graph_util from '../tf_graph_common/util';
import '../tf_graph_node_search/tf-graph-node-search';
import { getRouter } from '../tf_backend/router';
import { fetchPbTxt, parseGraphPbTxt } from '../tf_graph_common/parser';
import '../tf_graph_loader/tf-graph-dashboard-loader';
import * as tf_graph_parser from '../tf_graph_common/parser';
import * as tf_graph_node from '../tf_graph_common/node';
import * as tf_node_info from '../tf_graph_info/tf-node-info';

export interface Selection {
  run: string;
  tag: string | null;
  type: tf_graph_common.SelectionType;
  batch: number;
  step: number;
}
// TODO(stephanwlee): Move this to tf-graph-dashboard
export interface TagItem {
  tag: string | null;
  displayName: string;
  conceptualGraph: boolean;
  opGraph: boolean;
  profile: boolean;
}
// TODO(stephanwlee): Move this to tf-graph-dashboard
export interface RunItem {
  name: string;
  tags: TagItem[];
}
// TODO(stephanwlee): Move this to tf-graph-dashboard
export type Dataset = Array<RunItem>;
@customElement('tf-graph-controls')
class TfGraphControls extends LegacyElementMixin(DarkModeMixin(PolymerElement)) {
  static readonly template = html`
    <style>
      :host {
        color: #555;
        display: flex;
        flex-direction: column;
        font-size: 12px;
        width: 100%;
        --tb-graph-controls-title-color: #000;
        --tb-graph-controls-legend-text-color: #000;
        --tb-graph-controls-text-color: #555;
        --tb-graph-controls-title-font-size: 14px;
        --tb-graph-controls-subtitle-font-size: 14px;
        --paper-input-container-shared-input-style_-_font-size: 14px;
        --paper-font-subhead_-_font-size: 14px;
      }

      :host(.dark-mode) {
        --tb-graph-controls-title-color: #fff;
        --tb-graph-controls-legend-text-color: #f3f3f3;
        --tb-graph-controls-text-color: #eee;
      }

      paper-dropdown-menu {
        --paper-dropdown-menu-input: {
          padding: 0;
          color: gray;
        }
        --iron-icon-width: 15px;
        --iron-icon-height: 15px;
        --primary-text-color: gray;
        --paper-item-min-height: 30px;
      }

      paper-button[raised].keyboard-focus {
        font-weight: normal;
      }

      .run-dropdown {
        --paper-input-container: {
          padding: 5px 0 5px 5px;
        }
      }

      table {
        border-collapse: collapse;
        border-spacing: 0;
      }

      table tr {
        height: 20px;
      }

      table td {
        padding: 0;
        margin: 10px;
      }

      .allcontrols {
        padding: 0 20px 20px;
        flex-grow: 1;
        overflow-y: auto;
      }

      .holder {
        background: var(--secondary-background-color);
        box-sizing: border-box;
        color: var(--tb-graph-controls-text-color);
        width: 100%;
      }

      .legend-toolbar {
        appearance: none;
        background-color: inherit;
        border-top: 1px solid #ccc;
        border-bottom: 1px solid #ccc;
        border-right: none;
        border-left: none;
        cursor: pointer;
        color: var(--tb-graph-controls-legend-text-color);
        font: inherit;
        display: flex;
        align-items: center;
        justify-content: space-between;
        width: 100%;
        outline: none;
      }

      .legend-toolbar,
      .legend-content {
        padding: 8px 20px;
      }

      .toggle-legend-button {
        max-height: 20px;
        max-width: 20px;
        padding: 0;
      }

      .toggle-legend-text {
        font-size: var(--tb-graph-controls-subtitle-font-size);
      }

      paper-radio-button {
        display: block;
        padding: 5px;
      }

      .rectangle {
        width: 40px;
        height: 18px;
        display: inline-block;
        border-radius: 1px;
        margin-right: 10px;
      }

      .-rectangle {
        width: 35px;
        height: 16px;
        background-color: red;
        display: inline-block;
      }

      .color-box {
        width: 55px;
        height: 25px;
        border: 1px solid #555;
      }
      svg {
        transition: fill 0.3s, transform 0.2s; /* 为SVG图标添加过渡效果 */
      }

      svg:hover {
        fill: #ff6347; /* 改变颜色 */
        transform: scale(1.2); /* 放大 */
      }

      svg path {
        fill: #000; /* 默认颜色 */
      }

      /* 确保 path 不响应 hover */
      svg path:hover {
        transform: none; /* 禁用 path 元素的 hover 效果 */
        fill: #000; /* 保持默认颜色 */
      }

      /* 按下时 SVG 颜色变化 */
      svg:active {
        fill: #ff4500; /* 按下时颜色 */
      }

      svg.icon,
      tf-graph-icon {
        width: 50px;
        height: 18px;
      }
      .domainValues {
        margin-bottom: 10px;
        width: 165px;
      }
      .domainStart {
        float: left;
      }
      .domainEnd {
        float: right;
      }
      .colorBox {
        width: 20px;
      }

      .image-icon {
        width: 24px;
        height: 24px;
      }

      .help-icon {
        height: 15px;
        margin: 0;
        padding: 0;
      }

      .gray {
        color: #666;
      }

      .title {
        font-size: var(--tb-graph-controls-title-font-size);
        margin: 8px 5px 8px 0;
        color: var(--tb-graph-controls-title-color);
      }
      .title small {
        font-weight: normal;
      }

      #file {
        padding: 8px 0;
      }

      .color-legend-row {
        align-items: center;
        clear: both;
        display: flex;
        height: 20px;
        margin-top: 5px;
      }

      .color-legend-row .label,
      .color-legend-row svg,
      .color-legend-row tf-graph-icon {
        flex: 0 0 40px;
        margin-right: 10px;
      }

      .control-holder .icon-button {
        font-size: var(--tb-graph-controls-subtitle-font-size);
        margin: 0 -5px;
        padding: 5px;
        display: flex;
        justify-content: flex-start;
        color: var(--tb-graph-controls-text-color);
      }

      .button-text {
        padding-left: 20px;
        text-transform: none;
      }

      .upload-button {
        width: 165px;
        height: 25px;
        text-transform: none;
        margin-top: 4px;
      }

      .button-icon {
        width: 26px;
        height: 26px;
        color: var(--paper-orange-500);
      }

      .hidden-input {
        display: none;
      }

      .allcontrols .control-holder {
        clear: both;
        display: flex;
        justify-content: space-between;
      }

      .allcontrols .control-holder.control-options {
        padding: 0 0 15px 15px;
        flex-direction: column;
      }

      .allcontrols .control-holder paper-toggle-button {
        margin-bottom: 5px;
      }

      span.counter {
        font-size: var(--tb-graph-controls-subtitle-font-size);
        color: gray;
        margin-left: 4px;
      }

      .runs-row .title,
      .tags-row .title {
        display: flex;
        align-items: baseline;
      }

      .runs-row paper-item,
      .tags-row paper-item {
        --paper-item: {
          white-space: nowrap;
        }
      }

      table.control-holder {
        border: 0;
        border-collapse: collapse;
      }

      table.tf-graph-controls td.input-element-table-data {
        padding: 0 0 0 20px;
      }

      .spacer {
        flex-grow: 1;
      }

      .color-text {
        overflow: hidden;
      }

      .color-text.gradient-container {
        margin: 0 5px;
      }

      /** Override inline styles that suppress pointer events for disabled buttons. Otherwise, the */
      /*  tooltips do not appear. */
      paper-radio-group paper-radio-button {
        pointer-events: auto !important;
      }

      .legend-clarifier {
        color: #266236;
        cursor: pointer;
        height: 16px;
        width: 16px;
        margin-left: 4px;
        display: inline-block;
        text-decoration: underline;
        background: url('');
      }

      .legend-clarifier paper-tooltip {
        width: 150px;
      }

      .custom-tooltip {
        font-size: 12px;
      }

      /** Otherwise, polymer UI controls appear atop node search. */
      tf-graph-node-search {
        z-index: 1;
        width: 100%;
      }

      paper-dropdown-menu {
        flex-grow: 1;
      }

      .tabs {
        display: flex;
        border-bottom: 1px solid #ccc;
      }

      .tab-button {
        flex: 1;
        padding: 10px;
        background: #f1f1f1;
        border: none;
        cursor: pointer;
        border-bottom: 3px solid transparent; /* 初始状态下的底部线条透明 */
      }

      .tab-button.active {
        background: #fff;
        border-bottom: 3px solid orange; /* 聚焦时显示橙色线条 */
      }

      .tab-content {
        background: #fffcf3;
        padding-left: 20px;
        flex-grow: 1;
        overflow-y: auto;
      }

      .hidden {
        display: none;
      }

      .toggle-button {
        float: right;
        max-height: 20px;
        max-width: 20px;
        padding: 0;
      }

      .control-toggle-button {
        float: left;
        max-height: 20px;
        max-width: 20px;
        padding: 0;
      }

      details {
        background: #fffcf3;
        margin: 0;
        padding: 0;
      }

      details > details {
        padding-left: 5px;
      }

      details > details > * {
        margin: 0;
      }

      details > details > details {
        padding-left: 10px; /* 每层嵌套增加 5px*/
      }

      summary {
        display: flex;
        align-items: center;
        cursor: pointer;
        background: #fffcf3;
        white-space: nowrap; /* 防止内容换行 */
      }

      .no-arrow::before {
        content: none; // 不显示三角形
      }

      summary::before {
        content: '';
        display: inline-block;
        width: 0;
        height: 0;
        border-left: 4px solid black;
        border-right: 4px solid transparent;
        border-top: 4px solid transparent;
        border-bottom: 4px solid transparent;
        margin-right: 5px;
      }

      details[open] > summary::before {
        border-left: 4px solid transparent;
        border-right: 4px solid transparent;
        border-top: 4px solid black;
        border-bottom: 0;
        margin-right: 5px;
      }

      details > details > summary::before {
        border-left: 4px solid black;
        border-right: 4px solid transparent;
        border-top: 4px solid transparent;
        border-bottom: 4px solid transparent;
        margin-right: 5px;
      }

      details > details[open] > summary::before {
        border-left: 4px solid transparent;
        border-right: 4px solid transparent;
        border-top: 4px solid black;
        border-bottom: 0;
        margin-right: 5px;
      }

      details > details > details > summary::before {
        border-left: 4px solid black;
        border-right: 4px solid transparent;
        border-top: 4px solid transparent;
        border-bottom: 4px solid transparent;
        margin-right: 5px;
      }

      details > details > details[open] > summary::before {
        border-left: 4px solid transparent;
        border-right: 4px solid transparent;
        border-top: 4px solid black;
        border-bottom: 0;
        margin-right: 5px;
      }

      paper-checkbox {
        --paper-checkbox-unchecked-color: gray; /* 选中时的颜色 */
      }
    </style>

    <div class="tabs">
      <button class="tab-button" on-tap="_showNodeControls">设置</button>
      <button class="tab-button" on-tap="_showDirectoryStructure">目录</button>
      <button class="tab-button" on-tap="_showSearchStructure">搜索</button>
      <button class="tab-button" on-tap="_showMatch">匹配</button>
    </div>
    <div id="nodes-content" class="tab-content">
      <div class="allcontrols">
        <div class="control-holder">
          <paper-button class="icon-button" on-tap="_fit" alt="Fit to screen">
            <iron-icon icon="aspect-ratio" class="button-icon"></iron-icon>
            <span class="button-text">Fit to screen</span>
          </paper-button>
        </div>
        <div class="control-holder">
          <paper-button class="icon-button" on-click="download" alt="Download PNG">
            <iron-icon icon="file-download" class="button-icon"></iron-icon>
            <span class="button-text">Download PNG</span>
          </paper-button>
        </div>
        <template is="dom-if" if="[[showUploadButton]]">
          <div class="control-holder">
            <paper-button
              class="icon-button"
              on-click="_getFile"
              alt="Upload file"
              title="Upload a pbtxt file to view a graph from the local filesystem"
            >
              <iron-icon icon="file-upload" class="button-icon"></iron-icon>
              <span class="button-text">Upload file</span>
            </paper-button>

            <div class="hidden-input">
              <input type="file" id="file" name="file" on-change="_updateFileInput" accept=".pbtxt" />
            </div>
          </div>
        </template>
        <div class="control-holder runs-row">
          <div class="title">Run <span class="counter">([[datasets.length]])</span></div>
          <paper-dropdown-menu no-label-float no-animations noink horizontal-align="left" class="run-dropdown">
            <paper-listbox class="dropdown-content" selected="{{_selectedRunIndex}}" slot="dropdown-content">
              <template is="dom-repeat" items="[[datasets]]">
                <paper-item>[[item.name]]</paper-item>
              </template>
            </paper-listbox>
          </paper-dropdown-menu>
        </div>
        <template is="dom-if" if="[[showSessionRunsDropdown]]">
          <div class="control-holder tags-row">
            <div class="title">
              Tag
              <span class="counter">([[_numTags(datasets, _selectedRunIndex)]])</span>
            </div>
            <paper-dropdown-menu no-label-float no-animations horizontal-align="left" noink class="run-dropdown">
              <paper-listbox class="dropdown-content" selected="{{_selectedTagIndex}}" slot="dropdown-content">
                <template is="dom-repeat" items="[[_getTags(datasets, _selectedRunIndex)]]">
                  <paper-item on-click="_clearMicroStep">[[item.displayName]]</paper-item>
                </template>
              </paper-listbox>
            </paper-dropdown-menu>
          </div>
        </template>
        <div class="control-holder runs-row">
          <template is="dom-if" if="{{steplist.length}}">
            <div class="title">Step <span class="counter">([[steplist.length]])</span></div>
            <paper-dropdown-menu no-label-float no-animations noink horizontal-align="left" class="run-dropdown">
              <paper-listbox class="dropdown-content" selected="{{_selectedStep}}" slot="dropdown-content">
                <template is="dom-repeat" items="[[steplist]]">
                  <paper-item>[[item]]</paper-item>
                </template>
              </paper-listbox>
            </paper-dropdown-menu>
          </template>
          <template is="dom-if" if="{{microsteps.length}}">
            <div class="title">MicroStep<span class="counter">([[computedLength(microsteps)]])</span></div>
            <paper-dropdown-menu no-label-float no-animations noink horizontal-align="left" class="run-dropdown">
              <paper-listbox class="dropdown-content" selected="{{_selectedBatch}}" slot="dropdown-content">
                <template is="dom-repeat" items="[[microsteps]]">
                  <paper-item>[[item]]</paper-item>
                </template>
              </paper-listbox>
            </paper-dropdown-menu>
          </template>
        </div>
      </div>
    </div>
    <div id="directory-content" class="tab-content hidden">
      <div id="menubox" style="padding-top: 10px">
        <details>
          <summary id="root" on-click="_getdata">目录</summary>
        </details>
      </div>
    </div>
    <div id="search-content" class="tab-content hidden">
      <div class="control-holder" style="display: flex; align-items: center;">
        <tf-graph-node-search
          selected-node="{{selectedNode}}"
          expand-node="{{expandNode}}"
          render-hierarchy="[[renderHierarchy]]"
          menu="[[menu]]"
          selected-side="{{selectedSide}}"
        ></tf-graph-node-search>
        <paper-dropdown-menu id="searchOption" label="请选择" style="margin-left: 20px; width: 75px;">
          <paper-listbox slot="dropdown-content" id="dropdown">
            <paper-item value="NPU" on-click="_searchNpu">NPU</paper-item>
            <paper-item value="标杆" on-click="_searchBench">Bench</paper-item>
          </paper-listbox>
        </paper-dropdown-menu>
      </div>
      <template is="dom-if" if="{{colorSetChanged.length}}">
        <div class="container" style="display: flex; flex-direction: column;">
          <div class="title">精度筛选 <span class="counter">([[colorSetChanged.length]])</span></div>
          <div class="run-dropdown" style="margin-top: 8px; display: flex; flex-direction: column;">
            <template is="dom-repeat" items="[[colorSetChanged]]">
              <div class="color-option" style="display: flex; align-items: center;">
                <paper-checkbox id="checkbox-[[index]]" on-click="_toggleCheckbox"></paper-checkbox>
                <div
                  style="width: 12px; height: 12px; background-color: [[item.0]]; margin-right: 8px; border: 1px solid gray;"
                ></div>
                [[item.1.value]]
              </div>
            </template>
          </div>
        </div>
      </template>
      <template is="dom-if" if="{{precisionmenu || precisionmenu === ''}}">
        <div class="container" style="display: flex; align-items: center; padding-top: 10px;">
          <div class="title">按精度筛选 <span class="counter">([[precisionmenu.length]])</span></div>
          <paper-dropdown-menu
            no-label-float
            no-animations
            noink
            horizontal-align="left"
            class="run-dropdown"
            data-id="npu-dropdown"
          >
            <paper-listbox class="dropdown-content" slot="dropdown-content">
              <template is="dom-repeat" items="[[precisionmenu]]">
                <paper-item on-click="_observePrecsionNode">[[item]]</paper-item>
              </template>
            </paper-listbox>
          </paper-dropdown-menu>
        </div>
      </template>
      <div>
        <button data-action="previous" on-click="_handlePrecisonSearch">搜索上一个</button>
        <button data-action="next" on-click="_handlePrecisonSearch">搜索下一个</button>
      </div>
      <template is="dom-if" if="{{overFlowSet.length}}">
        <div class="container" style="display: flex; flex-direction: column;">
          <div class="title">溢出筛选 <span class="counter">([[overFlowSet.length]])</span></div>
          <div class="run-dropdown" style="margin-top: 8px; display: flex; flex-direction: column;">
            <template is="dom-repeat" items="[[overFlowSet]]">
              <div class="color-option" style="display: flex; align-items: center;">
                <paper-checkbox id="overflowCheckbox-[[index]]" on-click="_toggleCheckbox"></paper-checkbox>
                <div
                  style="width: 12px; height: 12px; background-color: [[item.0]]; margin-right: 8px; border: 1px solid gray;"
                ></div>
                [[item.1]]
              </div>
            </template>
          </div>
        </div>
      </template>
      <template is="dom-if" if="{{overflowmenu || overflowmenu === ''}}">
        <div class="container" style="display: flex; align-items: center; padding-top: 10px;">
          <div class="title">按溢出等级筛选 <span class="counter">([[overflowmenu.length]])</span></div>
          <paper-dropdown-menu
            no-label-float
            no-animations
            noink
            horizontal-align="left"
            class="run-dropdown"
            data-id="bench-dropdown"
          >
            <paper-listbox class="dropdown-content" slot="dropdown-content">
              <template is="dom-repeat" items="[[overflowmenu]]">
                <paper-item on-click="_observePrecsionNode">[[item]]</paper-item>
              </template>
            </paper-listbox>
          </paper-dropdown-menu>
        </div>
      </template>
      <div>
        <button data-action="previous" on-click="_handleOverflowSearch">搜索上一个</button>
        <button data-action="next" on-click="_handleOverflowSearch">搜索下一个</button>
      </div>
      <template is="dom-if" if="{{Bench_unmatched || Bench_unmatched === ''}}">
        <div class="container" style="display: flex; align-items: center; padding-top: 10px;">
          <div class="title">标杆侧未匹配节点 <span class="counter">([[Bench_unmatched.length]])</span></div>
          <paper-dropdown-menu
            no-label-float
            no-animations
            noink
            horizontal-align="left"
            class="run-dropdown"
            data-id="bench-dropdown"
          >
            <paper-listbox class="dropdown-content" slot="dropdown-content">
              <template is="dom-repeat" items="[[Bench_unmatched]]">
                <paper-item on-click="_observeBenchUnMatchedNode">[[item]]</paper-item>
              </template>
            </paper-listbox>
          </paper-dropdown-menu>
        </div>
      </template>
      <div>
        <button data-action="previous" on-click="_handleUnmatchSearch">搜索上一个</button>
        <button data-action="next" on-click="_handleUnmatchSearch">搜索下一个</button>
      </div>
    </div>
    <div id="match-content" class="tab-content hidden">
      <div class="title">未匹配节点:</div>
      <template is="dom-if" if="{{NPU_unmatched || NPU_unmatched === ''}}">
        <div class="container" style="display: flex; align-items: center;">
          <div class="title">NPU <span class="counter">([[NPU_unmatched.length]])</span></div>
          <paper-dropdown-menu no-label-float no-animations noink horizontal-align="left" class="run-dropdown">
            <paper-listbox class="dropdown-content" slot="dropdown-content" selected="{{_selectedNpuMatchMenu}}">
              <template is="dom-repeat" items="[[NPU_unmatched]]">
                <paper-item on-click="_observeNPUUnMatchedNode">[[item]]</paper-item>
              </template>
            </paper-listbox>
          </paper-dropdown-menu>
        </div>
      </template>
      <template is="dom-if" if="{{Bench_unmatched || Bench_unmatched === ''}}">
        <div class="container" style="display: flex; align-items: center;">
          <div class="title">标杆 <span class="counter">([[Bench_unmatched.length]])</span></div>
          <paper-dropdown-menu no-label-float no-animations noink horizontal-align="left" class="run-dropdown">
            <paper-listbox class="dropdown-content" slot="dropdown-content" selected="{{_selectedBenchMatchMenu}}">
              <template is="dom-repeat" items="[[Bench_unmatched]]">
                <paper-item on-click="_observeBenchUnMatchedNode">[[item]]</paper-item>
              </template>
            </paper-listbox>
          </paper-dropdown-menu>
        </div>
      </template>
      <div><button on-click="_handleMatchedNodesClick">匹配节点</button></div>
      <div class="title" style="padding-top: 30px">已匹配节点:</div>
      <template is="dom-if" if="{{matched || matched === ''}}">
        <div class="container" style="display: flex; align-items: center;">
          <div class="title">匹配列表 <span class="counter">([[matched.length]])</span></div>
          <paper-dropdown-menu no-label-float no-animations noink horizontal-align="left" class="run-dropdown">
            <paper-listbox class="dropdown-content" slot="dropdown-content">
              <template is="dom-repeat" items="[[matched]]">
                <paper-item>[[item]]</paper-item>
              </template>
            </paper-listbox>
          </paper-dropdown-menu>
        </div>
      </template>
      <template is="dom-if" if="{{NPU_matched || NPU_matched === ''}}">
        <div class="container" style="display: flex; align-items: center;">
          <div class="title">NPU <span class="counter">([[NPU_matched.length]])</span></div>
          <paper-dropdown-menu no-label-float no-animations noink horizontal-align="left" class="run-dropdown">
            <paper-listbox class="dropdown-content" slot="dropdown-content" selected="{{_selectedUnMatchMenu}}">
              <template is="dom-repeat" items="[[NPU_matched]]">
                <paper-item on-click="_observeNPUMatchedNode">[[item]]</paper-item>
              </template>
            </paper-listbox>
          </paper-dropdown-menu>
        </div>
      </template>
      <template is="dom-if" if="{{Bench_matched || Bench_matched === ''}}">
        <div class="container" style="display: flex; align-items: center;">
          <div class="title">标杆 <span class="counter">([[Bench_matched.length]])</span></div>
          <paper-dropdown-menu no-label-float no-animations noink horizontal-align="left" class="run-dropdown">
            <paper-listbox class="dropdown-content" slot="dropdown-content" selected="{{_selectedUnMatchMenu}}">
              <template is="dom-repeat" items="[[Bench_matched]]">
                <paper-item on-click="_observeBenchMatchedNode">[[item]]</paper-item>
              </template>
            </paper-listbox>
          </paper-dropdown-menu>
        </div>
      </template>
      <div><button on-click="_handleUnMatchedNodesClick">取消匹配</button></div>
    </div>
    <div class="holder">
      <button class="legend-toolbar" on-click="_toggleLegendOpen">
        <span class="toggle-legend-text">Legend</span>
        <iron-icon icon="[[_getToggleLegendIcon(_legendOpened)]]" class="toggle-legend-button"> </iron-icon>
      </button>
      <iron-collapse opened="[[_legendOpened]]" class="legend-content">
        <!-- Color-mode-specific legend items -->
        <div class="color-text">
          <div class="color-legend-row">
            <tf-graph-icon type="META" height="16" fill-override="#eee" stroke-override="#a6a6a6"></tf-graph-icon>
            <span class="color-legend-value">Module or Operators</span>
          </div>
        </div>

        <!-- Common legend items -->
        <div>
          <table>
            <tbody>
              <tr>
                <td>
                  <tf-graph-icon type="OP" height="16"></tf-graph-icon>
                </td>
                <td>
                  Unexpanded Module or Operators
                  <div class="legend-clarifier">
                    <paper-tooltip animation-delay="0" position="right" offset="0">
                      <div class="custom-tooltip">
                        Unexpandable Node: It can be an Api, operator or module. It cannot be expanded because it has no
                        subnodes
                      </div>
                    </paper-tooltip>
                  </div>
                </td>
              </tr>
              <tr>
                <td>
                  <tf-graph-icon type="API_LIST" height="16"></tf-graph-icon>
                </td>
                <td>
                  Api List
                  <div class="legend-clarifier">
                    <paper-tooltip animation-delay="0" position="right" offset="0">
                      <div class="custom-tooltip">Apis between modules</div>
                    </paper-tooltip>
                  </div>
                </td>
              </tr>
              <tr>
                <td>
                  <svg class="icon"></svg>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      </iron-collapse>
    </div>
    <div class="holder">
      <button class="legend-toolbar" on-click="_toggleColorsOpen" id="colors-toolbar">
        <iron-icon icon="[[_getToggleLegendIcon(_colors)]]" class="toggle-legend-button"> </iron-icon>
        <span class="toggle-legend-text">Colors</span>
        <svg
          xmlns="http://www.w3.org/2000/svg"
          width="6%"
          height="6%"
          viewBox="0 0 24 24"
          style="margin-left: auto"
          on-click="_clickSetting"
        >
          <path
            d="M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"
          ></path>
        </svg>
      </button>

      <div class="legend-toolbar hidden" id="colorSetting-toolbar" style="width: auto; cursor: default;">
        <span class="toggle-legend-text">颜色设置</span>
        <div style="display: flex; margin-left: auto;">
          <button on-click="_confirmAction">
            <span>确认</span>
          </button>
          <button on-click="_cancelAction">
            <span>取消</span>
          </button>
        </div>
      </div>

      <iron-collapse opened="[[_colors]]" class="legend-content" id="colors-content">
        <!-- Common legend items -->
        <div>
          <table id="coloritems">
            <tbody></tbody>
          </table>
        </div>
      </iron-collapse>
      <iron-collapse opened="[[_colors]]" class="legend-content hidden" id="colorSetting-content" style="height: 150px">
        <div>颜色选择 左区间 右区间 新增/删除</div>
        <div style="display: flex; align-items: center;">
          <!-- 主选项 -->
          <select id="color-select-0" class="color-box" on-change="_changeColor" style="margin-right: 10px;">
            <template is="dom-repeat" items="[[colorList]]" as="item">
              <option style="background-color: [[item]]" value="{{item}}"></option>
            </template>
          </select>

          <div style="display: flex; margin-right: 10px;">
            <input
              id="input-left"
              on-change="_validateInputs"
              style="display: inline-block; width: 60px; margin-right: 10px;"
            />
            <input id="input-right" on-change="_validateInputs" style="display: inline-block; width: 60px;" />
          </div>

          <button id="add-button-0" on-click="_addOption">+</button>
        </div>

        <!-- 动态生成的隐藏选项 -->
        <template is="dom-repeat" items="[[hiddenSelects]]" as="selectItem">
          <div style="display: flex; align-items: center;">
            <!-- 颜色选择框 -->
            <select
              id="color-select-[[selectItem]]"
              class="color-box"
              on-change="_changeColor"
              style="margin-right: 10px;"
            >
              <template is="dom-repeat" items="[[colorList]]" as="item">
                <option style="background-color: [[item]]" value="[[item]]"></option>
              </template>
            </select>
            <!-- 输入框 1 -->
            <div style="display: flex; align-items: center; margin-right: 10px;">
              <!-- 输入框 1 -->
              <input
                type="number"
                id="input-left"
                min="0"
                max="1"
                step="0.1"
                on-change="_validateInputs"
                style="margin-right: 10px;"
              />

              <!-- 输入框 2 -->
              <input type="number" id="input-right" min="0" max="1" step="0.1" on-change="_validateInputs" />
            </div>
            <!-- 删除按钮 -->
            <button on-click="_removeOption">-</button>
          </div>
        </template>
      </iron-collapse>
    </div>
    <div class="holder">
      <button class="legend-toolbar" on-click="_toggleOverflowLevelOpen">
        <span class="toggle-legend-text">Overflow_level</span>
        <iron-icon icon="[[_getToggleLegendIcon(_overFlowLevel)]]" class="toggle-legend-button"> </iron-icon>
      </button>
      <iron-collapse opened="[[_overFlowLevel]]" class="legend-content">
        <!-- Common legend items -->
        <div>
          <table id="coloritems">
            <tbody>
              <tr>
                <td>
                  <div class="rectangle" style="background-color: rgb(182,199,252);"></div>
                </td>
                <td>MEDIUM</td>
              </tr>
              <tr>
                <td>
                  <div class="rectangle" style="background-color: rgb(126,150,240);"></div>
                </td>
                <td>HIGH</td>
              </tr>
              <tr>
                <td>
                  <div class="rectangle" style="background-color: rgb(70,104,184);"></div>
                </td>
                <td>CRITICAL</td>
              </tr>
            </tbody>
          </table>
        </div>
      </iron-collapse>
    </div>
  `;
  @property({
    type: Array,
    observer: '_datasetsChanged',
  })
  datasets: any = [];
  @property({
    type: Array,
  })
  standardColorList = ['#FFFCF3', '#FFEDBE', '#FFDC7F', '#FFC62E', '#FF9B3D', '#FF704D', '#FF4118'];
  colorList = ['#FFFCF3', '#FFEDBE', '#FFDC7F', '#FFC62E', '#FF9B3D', '#FF704D', '#FF4118'];
  selectedColorList = [];
  hiddenSelects = [];
  @property({
    type: Object,
  })
  colorset;
  selectedColor = '';
  @property({
    type: Object,
  })
  matchednodeset: any = [];
  @property({
    type: Object,
  })
  unMatchednodeset: any = [];
  @property({
    type: Object,
  })
  colorSetChanged;
  @property({
    type: Object,
  })
  overFlowSet: any = [
    ['#B6C7FC', 'medium'],
    ['#7E96F0', 'high'],
    ['#4668B8', 'critical'],
  ];
  @property({
    type: Array,
  })
  selectColor: any = [];
  @property({
    type: Array,
  })
  overflowLevel: any = [];
  /**
   * @type {tf_graph_render.RenderGraphInfo}
   */
  @property({
    type: Object,
  })
  renderHierarchy: tf_graph_render.RenderGraphInfo;
  /**
   * @type {!Selection}
   */
  @property({
    type: Object,
    notify: true,
    readOnly: true,
    computed:
      '_computeSelection(datasets, _selectedRunIndex, _selectedTagIndex, _selectedGraphType, _selectedBatch, _selectedStep)',
  })
  selection: object;
  @property({
    type: Object,
    notify: true,
  })
  selectedFile: object;
  @property({
    type: Object,
  })
  _selectedColors;
  @property({
    type: Number,
    observer: '_selectedRunIndexChanged',
  })
  _selectedRunIndex: number = 0;
  @property({
    type: Number,
  })
  _selectedBatch: number = -1;
  @property({
    type: Number,
  })
  _selectedStep: number = -1;
  @property({
    type: Boolean,
    notify: true,
  })
  traceInputs: boolean = false;
  @property({
    type: Number,
    observer: '_selectedTagIndexChanged',
  })
  _selectedTagIndex: number = 0;
  /**
   * @type {tf_graph_common.SelectionType}
   */
  @property({
    type: String,
  })
  _selectedGraphType: string = tf_graph_common.SelectionType.OP_GRAPH;
  @property({
    type: String,
    notify: true,
  })
  selectedNode: string | null = null;
  selectedNPUNode: string = '';
  selectedBenchNode: string = '';
  selectedMatchedNPUNode: string = '';
  selectedMatchedBenchNode: string = '';
  @property({
    type: Boolean,
  })
  showSessionRunsDropdown: boolean = true;
  @property({
    type: Boolean,
  })
  showUploadButton: boolean = false;
  @property({
    type: Boolean,
  })
  _expanded: boolean = true;
  @property({
    type: Boolean,
  })
  _legendOpened: boolean = true;
  _colors: boolean = true;
  _overFlowLevel: boolean = true;
  @property({ type: Object })
  _downloadFilename = 'graph.png';
  menuData: any;
  graphDef: any;
  @property({ type: Object, observer: '_newMenu' })
  microsteps: any;
  steplist: any;
  @property({ type: Object, observer: '_updateColorItems' })
  colors: any;
  @property({ type: Object })
  nodeName: any;
  @property({ type: Object })
  menu: any;
  @property({ type: Object })
  unmatched: any = [];
  NPU_unmatched: any = [];
  Bench_unmatched: any = [];
  NPU_matched: any = [];
  Bench_matched: any = [];
  matched: any = [];
  matchedlist: any = [];
  @property({ type: Object })
  precisionmenu: any = [];
  overflowmenu: any = [];
  @property({ type: Number })
  selectedSide: number = 0;
  _selectedNpuMatchMenu: number = -1;
  _selectedBenchMatchMenu: number = -1;
  _selectedUnMatchMenu: number = -1;
  inputMax: number = 0;
  inputMin: number = 1;

  ready() {
    super.ready();
    document.addEventListener('contextMenuTag-changed', this._getTagChanged.bind(this));
  }

  _getTagChanged(contextMenuTag) {
    this.set('_selectedTagIndex', contextMenuTag.detail);
  }

  getMenuNodeName() {
    return this.nodeName;
  }
  _showTabContent(buttonText, contentId) {
    // Remove 'active' class from all buttons
    this.shadowRoot?.querySelectorAll('.tab-button').forEach((button) => {
      button.classList.remove('active');
    });

    // Add 'active' class to the clicked button
    const buttons = this.shadowRoot?.querySelectorAll('.tab-button');
    buttons?.forEach((button) => {
      if ((button as HTMLElement).innerHTML === buttonText) {
        button?.classList.add('active');
      }
    });

    // Hide all content
    this.shadowRoot?.querySelectorAll('.tab-content').forEach((content) => {
      content.classList.add('hidden');
    });

    // Show the selected content
    const selectedContent = this.shadowRoot?.getElementById(contentId);
    if (selectedContent) {
      selectedContent.classList.remove('hidden');
    }
  }

  // 使用示例
  _showNodeControls() {
    this._showTabContent('设置', 'nodes-content');
  }

  _showDirectoryStructure() {
    this._showTabContent('目录', 'directory-content');
  }

  _showSearchStructure() {
    this._showTabContent('搜索', 'search-content');
  }

  _showMatch() {
    this._showTabContent('匹配', 'match-content');
  }

  _onGraphTypeChangedByUserGesture() {
    tf_graph_util.notifyDebugEvent({
      actionId: tb_debug.GraphDebugEventId.GRAPH_TYPE_CHANGED,
      eventLabel: this._selectedGraphType,
    });
  }

  _numTags(datasets: Dataset, _selectedRunIndex: number) {
    return this._getTags(datasets, _selectedRunIndex).length;
  }
  _getTags(datasets: Dataset, _selectedRunIndex: number) {
    if (!datasets || !datasets[_selectedRunIndex]) {
      return [];
    }
    return datasets[_selectedRunIndex].tags;
  }
  _fit() {
    this.fire('fit-tap');
  }
  @observe('colorset')
  _observe() {
    if (this.colorset.length !== 0) {
      const colorsets = this.colorset;
      for (const item of colorsets) {
        if (item[1].value.length === 0) {
          item[1].value.push('无匹配节点');
        }
      }
      this.colorSetChanged = colorsets;
    } else {
      return;
    }
  }
  @observe('unmatched')
  _observeUnmatchedNode() {
    this.set('NPU_unmatched', this.unmatched[0]);
    this.set('Bench_unmatched', this.unmatched[1]);
  }
  @observe('matchedlist', 'selection')
  _observeMatchedList() {
    this.set('NPU_matched', []);
    this.set('Bench_matched', []);
    this.set('matched', []);
    if (this.matchedlist) {
      for (const item of this.matchedlist) {
        this.NPU_matched = [...this.NPU_matched, item[0]];
        this.Bench_matched = [...this.Bench_matched, item[1]];
        this.matched = [...this.matched, [item[0], item[1]]];
      }
      this.set('NPU_matched', this.NPU_matched);
      this.set('Bench_matched', this.Bench_matched);
      this.set('matched', this.matched);
    }
  }
  _observePrecsionNode(event) {
    let prefix = '';
    const hasBNode = this.renderHierarchy.renderedOpNames.some((name: string) => name.startsWith('B___'));
    if (hasBNode) {
      prefix = 'N___';
    }
    const node = prefix + event.model.item;
    this.set('selectedNode', node);
  }
  _observeNPUUnMatchedNode(event) {
    let prefix = '';
    const hasBNode = this.renderHierarchy.renderedOpNames.some((name: string) => name.startsWith('B___'));
    if (hasBNode) {
      prefix = 'N___';
    }
    const node = prefix + event.model.item;
    this.set('selectedNPUNode', node);
    this.set('selectedNode', node);
  }
  _observeBenchUnMatchedNode(event) {
    let prefix = '';
    const hasBNode = this.renderHierarchy.renderedOpNames.some((name: string) => name.startsWith('B___'));
    if (hasBNode) {
      prefix = 'B___';
    }
    const node = prefix + event.model.item;
    this.set('selectedBenchNode', node);
    this.set('selectedNode', node);
  }
  _observeNPUMatchedNode(event) {
    let prefix = '';
    const hasBNode = this.renderHierarchy.renderedOpNames.some((name: string) => name.startsWith('B___'));
    if (hasBNode) {
      prefix = 'N___';
    }
    const node = prefix + event.model.item;
    this.set('selectedMatchedNPUNode', node);
    const matched_node = this.findNodeInMatched(node.slice(4), 0);
    if (matched_node) {
      this.set('selectedMatchedBenchNode', `B___${matched_node}`);
    }
    this.set('selectedNode', node);
  }
  _observeBenchMatchedNode(event) {
    let prefix = '';
    const hasBNode = this.renderHierarchy.renderedOpNames.some((name: string) => name.startsWith('B___'));
    if (hasBNode) {
      prefix = 'B___';
    }
    const node = prefix + event.model.item;
    this.set('selectedMatchedBenchNode', node);
    const matched_node = this.findNodeInMatched(node.slice(4), 1);
    if (matched_node) {
      this.set('selectedMatchedNPUNode', `N___${matched_node}`);
    }
    this.set('selectedNode', node);
  }
  findNodeInMatched(node, side) {
    // 遍历 matched 数组
    for (let i = 0; i < this.matched.length; i++) {
      // 获取当前子数组
      const pair = this.matched[i];
      // 确保子数组的长度为 2，防止越界
      if (pair.length >= 2 && pair[side] === node) {
        if (side == 0) {
          return pair[1]; // 返回第二项
        } else {
          return pair[0]; // 返回第一项
        }
      }
    }
    // 如果没找到 node
    return null; // 返回 null 或其他指示未找到的值
  }
  _searchNpu() {
    this.selectedSide = 0;
  }
  _searchBench() {
    this.selectedSide = 1;
  }
  showDynamicDialog(message) {
    // 检查是否已经有显示的对话框，避免重复添加
    let existingDialog = this.shadowRoot?.querySelector('#dynamicDialog');
    if (existingDialog) {
      existingDialog.remove(); // 删除旧的对话框
    }
    // 创建新的对话框
    const dialog = document.createElement('paper-dialog');
    dialog.id = 'dynamicDialog';
    // 添加标题
    const title = document.createElement('h2');
    title.textContent = '提示';
    dialog.appendChild(title);
    // 添加提示内容
    const content = document.createElement('div');
    content.textContent = message;
    dialog.appendChild(content);
    // 添加按钮
    const buttonContainer = document.createElement('div');
    buttonContainer.classList.add('buttons');
    const closeButton = document.createElement('paper-button');
    closeButton.setAttribute('dialog-dismiss', '');
    closeButton.textContent = '关闭';
    buttonContainer.appendChild(closeButton);
    dialog.appendChild(buttonContainer);
    // 添加到 shadow DOM
    this.shadowRoot?.appendChild(dialog);
    // 打开对话框
    dialog.open();
  }

  _handleNodeSearch(event, type: 'unmatched' | 'precision' | 'overflow') {
    const action = event.target.getAttribute('data-action');
    const isUnmatched = type === 'unmatched';
    const menuFirstRow = isUnmatched ? this.menu[1] : this.menu[0];
    const selectedNode = this.selectedNode;
    let nodeList;
    let colorSet;
    if (isUnmatched) {
      nodeList = this.Bench_unmatched;
    } else if (type === 'overflow') {
      nodeList = this.overflowmenu;
      colorSet = this.overflowLevel;
    } else {
      nodeList = this.precisionmenu;
      if (type === 'precision') {
        colorSet = this.selectColor;
      } else {
        colorSet = null;
      }
    }
    const prefix = isUnmatched ? 'B___' : 'N___';
    const hasBNode =
      !isUnmatched && this.renderHierarchy.renderedOpNames.some((name: string) => name.startsWith('B___'));
    const showDialog = (message: string) => {
      this.showDynamicDialog(message);
    };

    const setDefaultNode = () => {
      const defaultNode = hasBNode && !isUnmatched ? `${prefix}${nodeList[0]}` : `${prefix}${nodeList[0]}`;
      this.set('selectedNode', defaultNode);
    };

    // 校验逻辑
    if (isUnmatched && nodeList.length === 0) {
      showDialog('标杆侧没有未匹配节点');
      return;
    }
    if (!isUnmatched && colorSet.length === 0) {
      showDialog('请选择颜色');
      return;
    }
    if (!isUnmatched && nodeList.length === 0) {
      showDialog('选择的颜色没有节点存在');
      return;
    }

    // 如果用户未选中节点，设置默认节点
    if (!selectedNode) {
      setDefaultNode();
      return;
    }

    // 获取 selectedNode 在 menuFirstRow 中的索引
    const slicedNode = hasBNode ? selectedNode.slice(4) : selectedNode;
    const startIndex = menuFirstRow.indexOf(slicedNode);
    if (startIndex === -1) {
      setDefaultNode();
      return;
    }

    // 查找下一个节点
    const findNextNode = () => {
      if (nodeList.includes(selectedNode)) {
        const currentIndex = nodeList.indexOf(selectedNode);
        if (currentIndex + 1 >= nodeList.length) {
          showDialog(isUnmatched ? '没有下一个未匹配节点' : '没有下一个问题节点');
          return null;
        }
        return nodeList[currentIndex + 1];
      }
      for (let i = startIndex + 1; i < menuFirstRow.length; i++) {
        if (nodeList.includes(menuFirstRow[i])) {
          return menuFirstRow[i];
        }
      }
      showDialog(isUnmatched ? '没有下一个未匹配节点' : '没有下一个问题节点');
      return null;
    };

    // 查找上一个节点
    const findPreviousNode = () => {
      if (nodeList.includes(selectedNode)) {
        const currentIndex = nodeList.indexOf(selectedNode);
        if (currentIndex === 0) {
          showDialog(isUnmatched ? '没有上一个未匹配节点' : '没有上一个问题节点');
          return null;
        }
        return nodeList[currentIndex - 1];
      }
      for (let i = startIndex - 1; i >= 0; i--) {
        if (nodeList.includes(menuFirstRow[i])) {
          return menuFirstRow[i];
        }
      }
      showDialog(isUnmatched ? '没有上一个未匹配节点' : '没有上一个问题节点');
      return null;
    };

    // 执行查找
    const nextNode = action === 'next' ? findNextNode() : findPreviousNode();

    if (nextNode) {
      let selectedNode;
      if (isUnmatched || hasBNode) {
        selectedNode = `${prefix}${nextNode}`;
      } else {
        selectedNode = nextNode;
      }
      this.set('selectedNode', selectedNode);
    }
  }

  _handleUnmatchSearch(event) {
    this._handleNodeSearch(event, 'unmatched');
  }

  _handlePrecisonSearch(event) {
    this._handleNodeSearch(event, 'precision');
  }

  _handleOverflowSearch(event) {
    this._handleNodeSearch(event, 'overflow');
  }

  async _handleMatchedNodesClick(this) {
    // 打开弹窗
    if (this.selectedNPUNode == '' || this.selectedBenchNode == '') {
      this.showDynamicDialog('节点不可匹配');
      return;
    }
    const params = new URLSearchParams();
    const run = this.datasets[this._selectedRunIndex].name;
    const tag = this.datasets[this._selectedRunIndex].tags[this._selectedTagIndex].tag;
    params.set('run', run);
    if (tag) params.set('tag', tag);
    params.set('NPU', this.selectedNPUNode);
    params.set('Bench', this.selectedBenchNode);
    const precisionPath = getRouter().pluginRouteForSrc('graph_ascend', '/match', params);
    const precisionStr = await tf_graph_parser.fetchPbTxt(precisionPath); // 获取异步的 ArrayBuffer
    const decoder = new TextDecoder();
    const decodedStr = decoder.decode(precisionStr); // 解码 ArrayBuffer 到字符串
    const dialogMessageMap: { [key: string]: string } = {
      inputshape: '匹配失败，inputshape不匹配',
      outputshape: '匹配失败，outputshape不匹配',
      inputNone: '匹配失败，input中包含None值',
      outputNone: '匹配失败，output中包含None值',
    };

    const message = dialogMessageMap[decodedStr];
    if (message) {
      this.showDynamicDialog(message); // 使用动态生成的对话框
    }
    let resultArray: any[] = [];
    resultArray = JSON.parse(decodedStr) as any[];
    if (resultArray.length !== 0) {
      this.push('matchednodeset', resultArray);
      if (this.unMatchednodeset.length !== 0) {
        const has = this.unMatchednodeset.indexOf(this.selectedNPUNode);
        if (has !== -1) {
          this.unMatchednodeset = [...this.unMatchednodeset.slice(0, has), ...this.unMatchednodeset.slice(has + 1)];
        }
      }
      tf_graph_node.getMatched(this.matchednodeset);
      tf_node_info.getMatched(this.matchednodeset);
      const index_N = this.NPU_unmatched.indexOf(this.selectedNPUNode.slice(4));
      if (index_N !== -1) {
        this.splice('NPU_unmatched', index_N, 1);
        this.notifyPath('NPU_unmatched');
      }
      const index_B = this.Bench_unmatched.indexOf(this.selectedBenchNode.slice(4));
      if (index_B !== -1) {
        this.splice('Bench_unmatched', index_B, 1);
        this.notifyPath('Bench_unmatched');
      }
      this.set('_selectedNpuMatchMenu', -1);
      this.set('_selectedBenchMatchMenu', -1);
      this.NPU_matched = [...this.NPU_matched, this.selectedNPUNode.slice(4)];
      this.Bench_matched = [...this.Bench_matched, this.selectedBenchNode.slice(4)];
      this.matched = [...this.matched, [this.selectedNPUNode.slice(4), this.selectedBenchNode.slice(4)]];
      this.showDynamicDialog('节点匹配成功');
      this.set('selectedNode', '');
      this.set('selectedNode', this.selectedNPUNode);
      this.selectedNPUNode = '';
      this.selectedBenchNode = '';
    } else {
      this.showDynamicDialog('节点不可匹配');
    }
  }

  async _handleUnMatchedNodesClick(this) {
    // 打开弹窗
    if (this.selectedMatchedNPUNode == '' || this.selectedMatchedBenchNode == '') {
      this.showDynamicDialog('取消匹配失败，请核对选择节点');
      return;
    }
    const existsInMatch = this.matched.some(
      ([NPU_matched, Bench_matched]) =>
        NPU_matched === this.selectedMatchedNPUNode.slice(4) &&
        Bench_matched === this.selectedMatchedBenchNode.slice(4),
    );
    if (!existsInMatch) {
      this.showDynamicDialog('取消匹配失败，请核对选择节点');
      return;
    }
    this.NPU_unmatched.push(this.selectedMatchedNPUNode.slice(4));
    this.NPU_unmatched = [...this.NPU_unmatched];
    this.notifyPath('NPU_unmatched');
    this.Bench_unmatched.push(this.selectedMatchedBenchNode.slice(4));
    this.Bench_unmatched = [...this.Bench_unmatched];
    this.notifyPath('Bench_unmatched');
    const index_N = this.NPU_matched.indexOf(this.selectedMatchedNPUNode.slice(4));
    if (index_N !== -1) {
      this.NPU_matched = [...this.NPU_matched.slice(0, index_N), ...this.NPU_matched.slice(index_N + 1)];
    }
    const index_B = this.Bench_matched.indexOf(this.selectedMatchedBenchNode.slice(4));
    if (index_B !== -1) {
      this.Bench_matched = [...this.Bench_matched.slice(0, index_B), ...this.Bench_matched.slice(index_B + 1)];
    }
    const index_M = this.matched.findIndex(
      (item) => item[0] === this.selectedMatchedNPUNode.slice(4) && item[1] === this.selectedMatchedBenchNode.slice(4),
    );
    if (index_M !== -1) {
      this.matched = [...this.matched.slice(0, index_M), ...this.matched.slice(index_M + 1)];
    }
    const index_U = this.matchednodeset.findIndex((item) => item[0] === this.selectedMatchedNPUNode);
    if (index_U !== -1) {
      this.matchednodeset = [...this.matchednodeset.slice(0, index_U), ...this.matchednodeset.slice(index_U + 1)];
    } else {
      this.unMatchednodeset.push(this.selectedMatchedNPUNode);
    }
    this.set('_selectedUnMatchMenu', -1);
    const params = new URLSearchParams();
    const run = this.datasets[this._selectedRunIndex].name;
    const tag = this.datasets[this._selectedRunIndex].tags[this._selectedTagIndex].tag;
    params.set('run', run);
    if (tag) params.set('tag', tag);
    params.set('NPU', this.selectedMatchedNPUNode);
    params.set('Bench', this.selectedMatchedBenchNode);
    const precisionPath = getRouter().pluginRouteForSrc('graph_ascend', '/unmatch', params);
    const precisionStr = await tf_graph_parser.fetchPbTxt(precisionPath); // 获取异步的 ArrayBuffer
    const decoder = new TextDecoder();
    const decodedStr = decoder.decode(precisionStr); // 解码 ArrayBuffer 到字符串
    tf_graph_node.getMatched(this.matchednodeset);
    tf_node_info.getMatched(this.matchednodeset);
    tf_graph_node.getUnMatched(this.unMatchednodeset);
    tf_node_info.getUnMatched(this.unMatchednodeset);
    this.set('selectedNode', '');
    this.showDynamicDialog('已取消匹配');
  }
  // 写一个如果切换数据清除所有checkbox和所有this.selectColor
  @observe('selection')
  _clearAllToggleCheckbox() {
    this._searchNpu();
    const dropdown = this.$.dropdown as any;
    dropdown.selected = 0;
    const allCheckboxes = this.shadowRoot?.querySelectorAll('paper-checkbox');
    if (allCheckboxes) {
      allCheckboxes.forEach((checkbox) => {
        checkbox.checked = false; // 清空每个 checkbox 的选中状态
      });
    }
    this.selectColor = [];
    this.precisionmenu = [];
    this.overflowLevel = [];
    this.set('selectedNode', '');
  }

  async _toggleCheckbox(this, event) {
    const { batch, step } = this.selection;
    const item = event.model.item;
    let checkbox, overflowCheckbox;
    if (item[1].value) {
      checkbox = this.shadowRoot?.getElementById(`checkbox-${event.model.index}`) as HTMLInputElement;
    } else {
      overflowCheckbox = this.shadowRoot?.getElementById(`overflowCheckbox-${event.model.index}`) as HTMLInputElement;
    }
    const run = this.datasets[this._selectedRunIndex].tags[this._selectedTagIndex].run;
    const tag = this.datasets[this._selectedRunIndex].tags[this._selectedTagIndex].tag;
    const params = new URLSearchParams();
    if (run) params.set('run', run);
    if (tag) params.set('tag', tag);
    params.set('batch', String(batch === -1 ? -1 : batch - 1));
    params.set('step', String(step === -1 ? -1 : step - 1));
    // 更新 selectColor 数组
    if (checkbox) {
      if (checkbox.checked) {
        this.selectColor.push(item[1].value); // 添加选中的颜色
      } else {
        const index = this.selectColor.findIndex(
          (color) => color[0] === item[1].value[0] && color[1] === item[1].value[1],
        );
        if (index !== -1) {
          this.selectColor.splice(index, 1); // 取消选中的颜色
        }
      }
      if (this.selectColor.length == 0) {
        this.precisionmenu = [];
        return;
      }
      params.set('precision_index', this.selectColor.join(','));
      const screenPath = getRouter().pluginRouteForSrc('graph_ascend', '/screen', params);
      try {
        const screenStr = tf_graph_parser.fetchPbTxt(screenPath);
        this.precisionmenu = JSON.parse(new TextDecoder().decode(await screenStr).replace(/'/g, '"')) as object;
      } catch (e) {
        console.error('Parse tooltips failed, please check the format of tooltips in the input vis file');
      }
      // 更新数据绑定
      this.notifyPath(`menu.${event.model.index}.checked`, checkbox.checked);
    } else {
      if (overflowCheckbox.checked) {
        this.overflowLevel.push(item[1]); // 添加选中的颜色
      } else {
        const index = this.overflowLevel.findIndex((overflow) => overflow === item[1]);
        if (index !== -1) {
          this.overflowLevel.splice(index, 1); // 取消选中的颜色
        }
      }
      if (this.overflowLevel.length == 0) {
        this.overflowmenu = [];
        return;
      }
      params.set('overflow_level', this.overflowLevel.join(','));
      const screenPath = getRouter().pluginRouteForSrc('graph_ascend', '/screen', params);
      try {
        const screenStr = tf_graph_parser.fetchPbTxt(screenPath);
        this.overflowmenu = JSON.parse(new TextDecoder().decode(await screenStr).replace(/'/g, '"')) as object;
      } catch (e) {
        console.error('Parse tooltips failed, please check the format of tooltips in the input vis file');
      }
      // 更新数据绑定
      this.notifyPath(`menu.${event.model.index}.checked`, overflowCheckbox.checked);
    }
  }

  download() {
    this.fire('download-image-requested', this._downloadFilename);
  }
  _clearMicroStep() {
    // 也清除一下MicroStep和Step
    this.set('_selectedBatch', -1);
    this.set('_selectedStep', -1);
    this.set('selectedNode', null);
  }
  computedLength(microsteps) {
    return microsteps.length > 0 ? microsteps.length - 1 : 0;
  }
  _updateFileInput(e: Event) {
    const file = (e.target as HTMLInputElement).files?.[0];
    if (!file) return;
    // Strip off everything before the last "/" and strip off the file
    // extension in order to get the name of the PNG for the graph.
    let filePath = file.name;
    const dotIndex = filePath.lastIndexOf('.');
    if (dotIndex >= 0) {
      filePath = filePath.substring(0, dotIndex);
    }
    const lastSlashIndex = filePath.lastIndexOf('/');
    if (lastSlashIndex >= 0) {
      filePath = filePath.substring(lastSlashIndex + 1);
    }
    this._setDownloadFilename(filePath);
    this.set('selectedFile', e);

    tf_graph_util.notifyDebugEvent({
      actionId: tb_debug.GraphDebugEventId.UPLOADED_GRAPH_FROM_FILESYSTEM,
    });
  }
  _datasetsChanged(newDatasets: Dataset, oldDatasets: Dataset) {
    if (oldDatasets !== null) {
      // Select the first dataset by default.
      this._selectedRunIndex = 0;
    }
    this._setDownloadFilename(this.datasets[this._selectedRunIndex]?.name);
  }
  _computeSelection(
    datasets: Dataset,
    _selectedRunIndex: number,
    _selectedTagIndex: number,
    _selectedGraphType: tf_graph_common.SelectionType,
    _selectedBatch: number,
    _selectedStep: number,
  ) {
    // _selectedStep = this.steplist[_selectedStep]
    if (!datasets[_selectedRunIndex] || !datasets[_selectedRunIndex].tags[_selectedTagIndex]) {
      return null;
    }
    return {
      run: datasets[_selectedRunIndex].name,
      tag: datasets[_selectedRunIndex].tags[_selectedTagIndex].tag,
      type: _selectedGraphType,
      batch: _selectedBatch,
      step: _selectedStep,
    };
  }
  _selectedRunIndexChanged(runIndex: number) {
    if (!this.datasets) return;
    this._selectedTagIndex = 0;
    this._selectedGraphType = this._getDefaultSelectionType();
    this.traceInputs = false; // Set trace input to off-state.
    this._setDownloadFilename(this.datasets[runIndex]?.name);
  }
  _selectedTagIndexChanged(): void {
    this._selectedGraphType = this._getDefaultSelectionType();
  }
  _getDefaultSelectionType(): tf_graph_common.SelectionType {
    const { datasets, _selectedRunIndex: run, _selectedTagIndex: tag } = this;
    if (!datasets || !datasets[run] || !(datasets[run] as any).tags[tag] || (datasets[run] as any).tags[tag].opGraph) {
      return tf_graph_common.SelectionType.OP_GRAPH;
    }
    const datasetForRun = datasets[run] as any;
    if (datasetForRun.tags[tag].profile) {
      return tf_graph_common.SelectionType.PROFILE;
    }
    if (datasetForRun.tags[tag].conceptualGraph) {
      return tf_graph_common.SelectionType.CONCEPTUAL_GRAPH;
    }
    return tf_graph_common.SelectionType.OP_GRAPH;
  }
  _getFile() {
    (this.$$('#file') as HTMLElement).click();
  }
  _setDownloadFilename(name?: string) {
    this._downloadFilename = (name || 'graph') + '.png';
  }
  _statsNotNull(stats: tf_graph_proto.StepStats) {
    return stats !== null;
  }
  _toggleLegendOpen(): void {
    this.set('_legendOpened', !this._legendOpened);
  }
  _toggleColorsOpen(): void {
    this.set('_colors', !this._colors);
  }
  getElements() {
    return {
      colorSettingToolbar: this.shadowRoot?.getElementById('colorSetting-toolbar'),
      colorSettingContent: this.shadowRoot?.getElementById('colorSetting-content'),
      colorsToolbar: this.shadowRoot?.getElementById('colors-toolbar'),
      colorsContent: this.shadowRoot?.getElementById('colors-content'),
    };
  }
  toggleVisibility(showColorSetting) {
    const { colorSettingToolbar, colorSettingContent, colorsToolbar, colorsContent } = this.getElements();

    if (colorSettingToolbar && colorSettingContent) {
      colorSettingToolbar.classList.toggle('hidden', !showColorSetting);
      colorSettingContent.classList.toggle('hidden', !showColorSetting);
    }
    if (colorsToolbar && colorsContent) {
      colorsToolbar.classList.toggle('hidden', showColorSetting);
      colorsContent.classList.toggle('hidden', showColorSetting);
    }
  }

  _clickSetting(event) {
    event.stopPropagation();
    this.set('_colors', true);
    this.toggleVisibility(true);
  }

  _cancelAction() {
    this.toggleVisibility(false);
  }

  _confirmAction() {}

  _changeColor(event) {
    const selectedColor = event.target.value;
    // 设置 color-box 背景色
    event.target.style.backgroundColor = selectedColor;
    this._setColorList();
  }

  _validateInputs(event) {
    const closestDiv = event.target.closest('div');
    let value = parseFloat(event.target.value);
    console.log('value', value);
    // 输入值验证
    if (isNaN(value)) {
      event.target.value = ''; // 非数字值清空
      return;
    }

    // 限制输入范围
    if (value < 0) {
      event.target.value = '0'; // 小于最小值，设置为最小值
      return;
    } else if (value > 1) {
      event.target.value = '1'; // 大于最大值，设置为最大值
      return;
    }

    // 获取左边和右边的输入框值
    let left = 0;
    let right = 0;

    // 获取左侧输入框值和右侧输入框值,如果左大于右，或者右大于左则清空输入
    if (event.target.id === 'input-left') {
      const otherSide = closestDiv.querySelector('#input-right') as HTMLInputElement;
      left = value;
      right = parseFloat(otherSide.value);
      console.log(right);
      if (left > right) {
        event.target.value = '';
      }
    } else {
      const otherSide = closestDiv.querySelector('#input-left') as HTMLInputElement;
      left = parseFloat(otherSide.value);
      right = value;
      if (right < left) {
        event.target.value = '';
      }
    }

    // 进一步操作可以使用 left_value 和 right_value
  }

  _addOption() {
    let colorSelectElements = this.shadowRoot?.querySelectorAll('[id^="color-select-"]');
    let count = colorSelectElements ? colorSelectElements.length : 0; // 确保 count 不是 undefined
    if (count < 5) {
      this.push('hiddenSelects', 0);
    }
    // 确保它在当前同步操作this.push()之后才执行.
    this.async(() => {
      this._setColorList();
    }, 0);
  }

  _removeOption(event) {
    const divToRemove = event.target.closest('div');
    divToRemove.remove();
    this._setColorList();
  }

  _setColorList() {
    let colorSelectElements = this.shadowRoot?.querySelectorAll('[id^="color-select-"]');
    let backgroundColors: string[] = [];
    colorSelectElements?.forEach((element) => {
      // 获取计算后的背景色
      const backgroundColor = this.rgbToHex(window.getComputedStyle(element).backgroundColor);
      backgroundColors.push(backgroundColor);
    });
    this.set(
      'colorList',
      this.standardColorList.filter((color) => !backgroundColors.includes(color)),
    );
    // 清除选中，否则再次选中不同列表的同一顺位的值的时候不会触发on-change
    this.async(() => {
      colorSelectElements?.forEach((element) => {
        if (element instanceof HTMLSelectElement) {
          element.selectedIndex = -1;
        }
      });
    }, 0);
  }

  rgbToHex(rgb) {
    const rgbArray = rgb
      .slice(4, -1)
      .split(',')
      .map((num) => parseInt(num.trim()));
    const toHex = (x) => {
      const hex = x.toString(16);
      return hex.length === 1 ? '0' + hex : hex;
    };
    return `#${toHex(rgbArray[0])}${toHex(rgbArray[1])}${toHex(rgbArray[2])}`.toUpperCase();
  }

  _toggleOverflowLevelOpen(): void {
    this.set('_overFlowLevel', !this._overFlowLevel);
  }
  _getToggleLegendIcon(legendOpened: boolean): string {
    // This seems counter-intuitive, but actually makes sense because the
    // expand-more button points downwards, and the expand-less button points
    // upwards. For most collapsibles, this works because the collapsibles
    // expand in the downwards direction. This collapsible expands upwards
    // though, so we reverse the icons.
    return legendOpened ? 'expand-more' : 'expand-less';
  }
  _getSelectionOpGraphDisabled(datasets: Dataset, _selectedRunIndex: number, _selectedTagIndex: number) {
    return (
      !datasets[_selectedRunIndex] ||
      !datasets[_selectedRunIndex].tags[_selectedTagIndex] ||
      !datasets[_selectedRunIndex].tags[_selectedTagIndex].opGraph
    );
  }
  _getSelectionProfileDisabled(datasets: Dataset, _selectedRunIndex: number, _selectedTagIndex: number) {
    return (
      !datasets[_selectedRunIndex] ||
      !datasets[_selectedRunIndex].tags[_selectedTagIndex] ||
      !datasets[_selectedRunIndex].tags[_selectedTagIndex].profile
    );
  }
  _getSelectionConceptualGraphDisabled(datasets: Dataset, _selectedRunIndex: number, _selectedTagIndex: number) {
    return (
      !datasets[_selectedRunIndex] ||
      !datasets[_selectedRunIndex].tags[_selectedTagIndex] ||
      !datasets[_selectedRunIndex].tags[_selectedTagIndex].conceptualGraph
    );
  }
  _getToggleIcon(expanded) {
    return expanded ? 'expand-less' : 'expand-more';
  }
  _toggleExpanded() {
    this._expanded = !this._expanded;
  }
  triggerMenuExpandEvent(newName) {
    const detailsElement = this.shadowRoot?.getElementById(newName) as HTMLDetailsElement;
    if (detailsElement?.open) {
      const event = new CustomEvent('menu-expand-node-changed', {
        detail: { name: newName, open: 'unexpand' },
      });
      document.dispatchEvent(event);
    } else {
      const event = new CustomEvent('menu-expand-node-changed', {
        detail: { name: newName, open: 'expand' },
      });
      document.dispatchEvent(event);
    }
  }
  _getdata(this, event) {
    const { batch, step } = this.selection;
    var nodeName = event.currentTarget.getAttribute('id');
    if (nodeName !== 'root') {
      this.triggerMenuExpandEvent(nodeName);
    }
    const run = this.datasets[this._selectedRunIndex].name;
    const tag = this.datasets[this._selectedRunIndex].tags[this._selectedTagIndex].tag;
    const params = new URLSearchParams();
    params.set('run', run);
    if (tag) params.set('tag', tag);
    params.set('node', nodeName);
    params.set('batch', String(batch === -1 ? -1 : batch - 1));
    params.set('step', String(step === -1 ? -1 : step - 1));
    const subnode_list = getRouter().pluginRouteForSrc('graph_ascend', '/subgraph', params);
    fetchPbTxt(subnode_list).then((arrayBuffer: ArrayBuffer) => {
      parseGraphPbTxt(arrayBuffer).then((graphDef) => {
        this.updateGraphData(graphDef, nodeName);
        return graphDef;
      });
    });
  }
  _newMenu() {
    let menubox;
    menubox = this.shadowRoot?.getElementById('menubox');
    menubox.innerHTML = '';
    const summary = document.createElement('summary');
    const detail = document.createElement('details');
    summary.id = 'root';
    summary.textContent = '目录';
    summary.addEventListener('click', this._getdata.bind(this));
    detail.appendChild(summary);
    menubox?.appendChild(detail);
  }
  _updateColorItems() {
    const coloritems = this.shadowRoot?.getElementById('coloritems');
    const tbody = coloritems?.querySelector('tbody');
    if (Object.entries(this.colors).length !== 0) {
      if (tbody) {
        tbody.innerHTML = '';
        Object.entries(this.colors).forEach(([color, details]) => {
          let detailsArray: any[] = [];
          detailsArray = [details];
          if (detailsArray) {
            const tr = document.createElement('tr');
            const td = document.createElement('td');
            const div = document.createElement('div');
            div.className = 'rectangle';
            div.style.backgroundColor = color;
            const td2 = document.createElement('td');
            const td3 = document.createElement('td');
            const divInTd3 = document.createElement('div');
            const paperTooltip = document.createElement('paper-tooltip');
            const divInPaperTooltip = document.createElement('div');
            divInTd3.className = 'legend-clarifier';
            paperTooltip.setAttribute('animation-delay', '0');
            paperTooltip.setAttribute('position', 'right');
            paperTooltip.setAttribute('offset', '0');
            divInPaperTooltip.className = 'custom-tooltip';
            divInPaperTooltip.textContent = detailsArray[0].description;
            if (detailsArray[0].value[0] !== undefined) {
              td2.textContent = detailsArray[0].value[0] + '-' + detailsArray[0].value[1];
            } else {
              td2.textContent = '无匹配节点';
            }
            tbody.appendChild(tr);
            tr.appendChild(td);
            td.appendChild(div);
            tr.appendChild(td2);
            tr.appendChild(td3);
            td3.appendChild(divInTd3);
            divInTd3.appendChild(paperTooltip);
            paperTooltip.appendChild(divInPaperTooltip);
          }
        });
      }
    } else {
      if (tbody) {
        tbody.innerHTML = '';
        const rows = [
          { color: '#FFFCF3', text: '1-0.8' },
          { color: '#FFEDBE', text: '0.8-0.6' },
          { color: '#FFDC7F', text: '0.6-0.4' },
          { color: '#FFC62E', text: '0.4-0.2' },
          { color: '#ff704d', text: '0.2-0' },
          { color: '#C7C7C7', text: 'Not Connected' },
        ];
        rows.forEach(({ color, text }) => {
          const tr = document.createElement('tr');
          tr.innerHTML = `<td><div class="rectangle" style="background-color: ${color};"></div></td><td>${text}</td>`;
          tbody.appendChild(tr);
        });
      }
      this.colorset = [
        [
          '#FFFCF3',
          {
            description:
              '此节点所有输入输出的统计量相对误差，值越大代表测量值与标杆值的偏差越大，相对误差计算方式：|(测量值-标杆值)/标杆值|',
            value: [0.8, 1],
          },
        ],
        [
          '#FFEDBE',
          {
            description:
              '此节点所有输入输出的统计量相对误差，值越大代表测量值与标杆值的偏差越大，相对误差计算方式：|(测量值-标杆值)/标杆值|',
            value: [0.6, 0.8],
          },
        ],
        [
          '#FFDC7F',
          {
            description:
              '此节点所有输入输出的统计量相对误差，值越大代表测量值与标杆值的偏差越大，相对误差计算方式：|(测量值-标杆值)/标杆值|',
            value: [0.4, 0.6],
          },
        ],
        [
          '#FFC62E',
          {
            description:
              '此节点所有输入输出的统计量相对误差，值越大代表测量值与标杆值的偏差越大，相对误差计算方式：|(测量值-标杆值)/标杆值|',
            value: [0.2, 0.4],
          },
        ],
        [
          '#ff704d',
          {
            description:
              '此节点所有输入输出的统计量相对误差，值越大代表测量值与标杆值的偏差越大，相对误差计算方式：|(测量值-标杆值)/标杆值|',
            value: [0, 0.2],
          },
        ],
        [
          '#C7C7C7',
          {
            description: '对比过程中节点未匹配上',
            value: [],
          },
        ],
      ];
    }
  }
  updateGraphData(graphDef, nodeName) {
    this.graphDef = graphDef;
    let menubox;
    let detailsElement;
    if (nodeName == 'root') {
      menubox = this.shadowRoot?.getElementById('menubox');
      detailsElement = menubox?.querySelector('details');
    } else {
      detailsElement = this.shadowRoot?.getElementById(nodeName);
    }
    this.graphDef.node.forEach((node) => {
      const detail = document.createElement('details');
      const summary = document.createElement('summary');
      let nodeNameWithoutPrefix;
      detail.id = node.name;
      summary.id = node.name;
      if (!this.shadowRoot?.getElementById(node.name)) {
        if (nodeName == 'root' && node.name.substring(0, 4) == 'N___') {
          nodeNameWithoutPrefix = node.name.substring(4) + '(对比)';
        } else if (nodeName == 'root' && node.name.substring(0, 4) == 'B___') {
          nodeNameWithoutPrefix = node.name.substring(4) + '(标杆)';
        } else {
          if (node.name.substring(0, 4) == 'B___' || node.name.substring(0, 4) == 'N___') {
            nodeNameWithoutPrefix = node.name.substring(4);
          } else {
            nodeNameWithoutPrefix = node.name;
          }
        }

        if (node.isLeaf) {
          summary.classList.add('no-arrow');
          detail.style.paddingLeft = '22px';
        } else {
          summary.classList.remove('no-arrow');
        }
        summary.style.backgroundColor = 'white';
        if (Object.keys(this.colors).length == 0) {
          this.colors = {
            '#FFFCF3': {
              value: [0.8, 1],
              description:
                '此节点所有输入输出的统计量相对误差，值越大代表测量值与标杆值的偏差越大，相对误差计算方式：|(测量值-标杆值)/标杆值|',
            },
            '#FFEDBE': {
              value: [0.6, 0.8],
              description:
                '此节点所有输入输出的统计量相对误差，值越大代表测量值与标杆值的偏差越大，相对误差计算方式：|(测量值-标杆值)/标杆值|',
            },
            '#FFDC7F': {
              value: [0.4, 0.6],
              description:
                '此节点所有输入输出的统计量相对误差，值越大代表测量值与标杆值的偏差越大，相对误差计算方式：|(测量值-标杆值)/标杆值|',
            },
            '#FFC62E': {
              value: [0.2, 0.4],
              description:
                '此节点所有输入输出的统计量相对误差，值越大代表测量值与标杆值的偏差越大，相对误差计算方式：|(测量值-标杆值)/标杆值|',
            },
            '#ff704d': {
              value: [0, 0.2],
              description:
                '此节点所有输入输出的统计量相对误差，值越大代表测量值与标杆值的偏差越大，相对误差计算方式：|(测量值-标杆值)/标杆值|',
            },
            '#C7C7C7': {
              value: [],
              description: '对比过程中节点未匹配上',
            },
          };
        }
        for (const [color, details] of Object.entries(this.colors)) {
          let detailsArray: any[] = [];
          detailsArray = [details];
          const [start, end] = detailsArray[0].value;
          if (
            (start == end && node.precision_index == start) ||
            (node.precision_index >= start && node.precision_index < end) ||
            (node.precision_index == end && end == 1)
          ) {
            summary.style.backgroundColor = color;
            break;
          } else {
            summary.style.backgroundColor = '#C7C7C7';
          }
        }
        summary.textContent = nodeNameWithoutPrefix;
        summary.addEventListener('click', this._getdata.bind(this));
        detail.appendChild(summary);
        detailsElement?.appendChild(detail);
      }
    });
  }
}
